package weiyao.xinxidasai.Utils.Encryption.Chaos;
import org.springframework.stereotype.Component;
import weiyao.xinxidasai.pojo.Version;

import java.io.*;
import java.security.SecureRandom;
import java.util.List;
import java.util.Random;
@Component
public class tokenCEOEncryptor {
    // 带token的加密方法
    public static void encryptStream(InputStream inputStream, OutputStream outputStream,
                                     Version version, String token) throws Exception {
        validateToken(token);

        // 创建混沌映射实例
        CEOLogisticMap ceoMap = new CEOLogisticMap(
                version.getR(),
                version.getX0(),
                version.getIterations()
        );

        // 生成密钥流
        List<Byte> keyStream = ceoMap.generateKeyStream();
        int keyStreamSize = keyStream.size();

        // 生成随机盐值(32字节)
        byte[] salt = new byte[32];
        new SecureRandom().nextBytes(salt);
        outputStream.write(salt);

        byte[] buffer = new byte[4096];
        int bytesRead;
        int totalBytes = 0;

        while ((bytesRead = inputStream.read(buffer)) != -1) {
            // 先置换（使用token增强盐值）
            byte[] permuted = permuteBuffer(buffer, bytesRead, salt, token);

            // 后异或
            for (int i = 0; i < bytesRead; i++) {
                byte keyByte = keyStream.get(totalBytes % keyStreamSize);
                permuted[i] = (byte) (permuted[i] ^ keyByte);
                totalBytes++;
            }

            outputStream.write(permuted, 0, bytesRead);
            outputStream.flush();
        }
        outputStream.flush();
    }

    // 带token的解密方法
    public static void decryptStream(InputStream inputStream, OutputStream outputStream,
                                     Version version, String token) throws Exception {
        validateToken(token);

        // 创建混沌映射实例（不使用token）
        CEOLogisticMap ceoMap = new CEOLogisticMap(
                version.getR(),
                version.getX0(),
                version.getIterations()
        );

        // 生成密钥流
        List<Byte> keyStream = ceoMap.generateKeyStream();
        int keyStreamSize = keyStream.size();

        // 读取盐值
        byte[] salt = new byte[32];
        int saltBytesRead = inputStream.read(salt);
        boolean skipPermutation = (saltBytesRead != 32); // 标记是否跳过置换

        byte[] buffer = new byte[4096];
        int bytesRead;
        int totalBytes = 0;

        while ((bytesRead = inputStream.read(buffer)) != -1) {
            // 先异或（解密）
            for (int i = 0; i < bytesRead; i++) {
                byte keyByte = keyStream.get(totalBytes % keyStreamSize);
                buffer[i] = (byte) (buffer[i] ^ keyByte);
                totalBytes++;
            }

            // 根据标记决定是否置换来解密被篡改数据
            byte[] processedBuffer;
            if (skipPermutation) {
                processedBuffer = buffer; // 跳过置换，直接使用异或后的数据
            } else {
                processedBuffer = unpermuteBuffer(buffer, bytesRead, salt, token);
            }

            outputStream.write(processedBuffer, 0, bytesRead);
            outputStream.flush();
        }
        outputStream.flush();
    }

    // 置换缓冲区（带token增强）
    private static byte[] permuteBuffer(byte[] buffer, int length, byte[] salt, String token) {
        // 1. 生成0~length-1的索引数组
        int[] indices = new int[length];
        for (int i = 0; i < length; i++) {
            indices[i] = i;
        }

        // 2. 使用token增强的盐值作为随机源
        byte[] tokenBytes = token.getBytes();
        long seed = createSeed(salt);
        for (byte b : tokenBytes) {
            seed = seed * 31 + b; // 简单但有效的组合
        }

        // 3. 洗牌索引
        Random rand = new Random(seed);
        for (int i = length - 1; i > 0; i--) {
            int j = rand.nextInt(i + 1);
            int temp = indices[i];
            indices[i] = indices[j];
            indices[j] = temp;
        }

        // 4. 按洗牌后索引重新排列字节
        byte[] result = new byte[length];
        for (int i = 0; i < length; i++) {
            result[i] = buffer[indices[i]];
        }
        return result;
    }

    // 逆置换缓冲区（带token增强）
    private static byte[] unpermuteBuffer(byte[] buffer, int length, byte[] salt, String token) {
        // 1. 生成0~length-1的索引数组
        int[] indices = new int[length];
        for (int i = 0; i < length; i++) {
            indices[i] = i;
        }

        // 2. 使用相同方式生成随机源（必须与加密时一致）
        byte[] tokenBytes = token.getBytes();
        long seed = createSeed(salt);
        for (byte b : tokenBytes) {
            seed = seed * 31 + b;
        }

        // 3. 洗牌索引（与加密相同的洗牌序列）
        Random rand = new Random(seed);
        for (int i = length - 1; i > 0; i--) {
            int j = rand.nextInt(i + 1);
            int temp = indices[i];
            indices[i] = indices[j];
            indices[j] = temp;
        }

        // 4. 恢复原始顺序（逆操作）
        byte[] result = new byte[length];
        for (int i = 0; i < length; i++) {
            result[indices[i]] = buffer[i];
        }
        return result;
    }

    // 从盐值创建确定性的种子
    private static long createSeed(byte[] salt) {
        long seed = 0;
        for (byte b : salt) {
            seed = (seed << 8) | (b & 0xFF);
        }
        return seed;
    }

    // Token验证
    private static void validateToken(String token) {
        if (token == null || token.length() != 32) {
            throw new IllegalArgumentException("Token must be exactly 32 characters");
        }
    }

    // 文件加密方法（带token）
    public static void encryptFile(File inputFile, File outputFile,
                                   Version version, String token) throws IOException {
        try (InputStream is = new FileInputStream(inputFile);
             OutputStream os = new FileOutputStream(outputFile)) {
            encryptStream(is, os, version, token);
        } catch (Exception e) {
            throw new IOException("加密失败: " + e.getMessage(), e);
        }
    }

    // 文件解密方法（带token）
    public static void decryptFile(File inputFile, File outputFile,
                                   Version version, String token) throws IOException {
        try (InputStream is = new FileInputStream(inputFile);
             OutputStream os = new FileOutputStream(outputFile)) {
            decryptStream(is, os, version, token);
        } catch (Exception e) {
            throw new IOException("解密失败: " + e.getMessage(), e);
        }
    }
}